﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


namespace Xww.LCV.Generator {
    public class Absolute : BaseGenerator, ILCSGenerator {

        #region ILCSGenerator 成员

        public override LCSCollection FromStrings(IEnumerable<string> QuintessentialSet, MiningParam param) {
            if (param.MiningType != MiningType.AbsolutePosition) {
                throw new ArgumentException("The param used in Absolute Generator should be type of MiningType.AbsolutePosition", "param");
            }
            int seqCount = QuintessentialSet.Count();
            if (param.Threshold <= 1 || param.Threshold > seqCount) {
                throw new ArgumentOutOfRangeException("Threshold", "Threshold must be greater than 1 and less than sequence count of QuintessentialSet.");
            }

            int seqWidth = QuintessentialSet.ElementAt(0).Length;
            int[] index = new int[seqWidth];
            int threshold = param.Threshold;

            List<LCS> final = new List<LCS>();
            List<LCS> former = FirstStep(QuintessentialSet.ToArray(), index, threshold);

            for (int iteration = 1; iteration < seqWidth; iteration++) {
                if (former.Count < 2) { break; }

                List<LCS> current = new List<LCS>();
                int formerCount = 0;
                for (int location = 0; location < seqWidth; location++) {
                    int start = index[location];
                    int end = (location == seqWidth - 1 ? seqWidth : index[location + 1]);
                    for (int self = start; self < end; self++) {
                        for (int other = 0; other < former.Count; other++) {
                            if (iteration == 1 && other < end) {
                                other = end - 1;
                                continue;
                            }
                            else if (iteration > 1) {
                                if (other <= self) {
                                    other = self;
                                    continue;
                                }
                                if (other >= end) {
                                    break;
                                }
                            }
                            //
                            if (LCS.CanImerge(former[self], former[other])) {
                                LCS item = LCS.Imerge(former[self], former[other]);
                                if (item.IsFitForQSet(QuintessentialSet, threshold)) {
                                    current.Add(item);
                                }
                            }
                        }
                    }
                    if (location == 0) { index[location] = 0; }
                    else { index[location] = formerCount; }
                    formerCount = current.Count;
                }
                final.AddRange(current);
                former = current;
            }
            string info = string.Format("Get {0} LCSs at threshold of {1}, {2}", final.Count, threshold, DateTime.Now.ToString());
            return new LCSCollection(final, info);
        }


        #endregion

        #region private functions

        private List<LCS> FirstStep(string[] QuintessentialSet, int[] index, int threshold) {
            int seqCount = QuintessentialSet.Length;
            int seqWidth = QuintessentialSet[0].Length;
            int formerCount = 0;
            List<LCS> rst = new List<LCS>();
            for (int location = 0; location < seqWidth; location++) {
                Dictionary<char, int> AACounter = CreateEmptyAACounter();
                for (int i = 0; i < seqCount; i++) {
                    char current = QuintessentialSet[i][location];
                    if (AACounter.ContainsKey(current)) {
                        AACounter[current] += 1;
                    }
                }
                foreach (char aa in AACounter.Keys) {
                    if (AACounter[aa] >= threshold) {
                        rst.Add(new LCS(AACounter[aa], aa, location +1));
                    }
                }
                if (location == 0) { index[location] = 0; }
                else { index[location] = formerCount; }
                formerCount = rst.Count;
            }
            return rst;
        }




        #endregion
    }
}
